package com.tsyz.netty.server;

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 项目名称: java-learning-records <br>
 * *********************************<br>
 * 包路径: com.tsyz.netty.server <br>
 * *********************************<br>
 * 描述: <br>
 *
 * @author ext.zhaoke14 <br>
 * @version 1.0 <br>
 * @date 2025/10/14 下午8:07
 */

public class ServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 用于存储已处理消息的唯一标识，防止重复处理相同消息
     */
    private static final Set<String> processedMessages = Collections.newSetFromMap(new ConcurrentHashMap<>());

    /**
     * 在通道活跃时，开启心跳检测任务。
     *
     * @param ctx ChannelHandlerContext 上下文对象，用于获取当前通道和执行其他操作。
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 当通道活跃时，开启心跳检测任务
        scheduleHeartbeat(ctx);
    }

    /**
     * 处理从客户端接收的消息。
     *
     * @param ctx ChannelHandlerContext，用于向客户端发送响应。
     * @param msg Object，接收到的消息对象。
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        String message = (String) msg;

        // 处理心跳消息
        if ("心跳".equals(message)) {
            System.out.println("服务器-接受-心跳:" + message);
            ctx.writeAndFlush("心跳确认");
            return;
        }

        // 避免重复处理相同消息
        String messageId = Integer.toString(message.hashCode());
        if (processedMessages.add(messageId)) {
            System.out.println("服务器-接受-文字: " + message);
            ctx.writeAndFlush("服务器-接受-文字: " + message);

            // 清理旧消息ID，防止内存泄漏
            cleanupOldMessages();
        }
    }

    /**
     * 定期清理过期的消息ID。
     */
    private void cleanupOldMessages() {
        // 定期清理过期的消息ID
        // 这里可以考虑使用一个调度器来定期清理
        // 例如，每隔一分钟清理一次
        // 此处省略具体实现
    }

    /**
     * 处理客户端连接异常事件。
     *
     * @param ctx ChannelHandlerContext，用于关闭连接。
     * @param t   Throwable，捕获的异常对象。
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable t) {
        // 使用更详细的日志记录
        System.err.println("客户端-异常-捕获: " + t.getMessage());
        ctx.close().addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * 定期发送心跳消息来检测连接是否活着。
     *
     * @param ctx ChannelHandlerContext 对象，用于获取当前通道和执行器。
     */
    private void scheduleHeartbeat(ChannelHandlerContext ctx) {
        // 定期发送心跳消息来检测连接是否活着
        ctx.executor().schedule(() -> {
            if (ctx.channel().isActive()) {
                ctx.writeAndFlush("心跳").addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                scheduleHeartbeat(ctx); // 继续调度下一个心跳
            }
        }, 5, TimeUnit.SECONDS); // 每5秒发送一次心跳
    }
}